# koroFileHeader at Yungoal acer
# Create: 2021-07-08 11:26:13
# LastEdit: 2021-07-08 16:13:34
"""备份清单
调用REST API的Backup Jobs - List接口：
https://docs.microsoft.com/zh-cn/rest/api/backup/2021-02-10/backup-jobs/list
"""
__author__ = '749B'

import os, sys
BASE_DIR = os.getcwd()
sys.path.append(BASE_DIR)

import argparse
from dateutil.parser import parse as date_parse
import textwrap
import msrestazure.azure_cloud
# pylint: disable = import-error
from azure_client import Azure_Client
from utils.blueking import job_start, job_success, job_fail
from utils.tools import remove_empty
from utils.env import init_environs

from typing import Optional, List
# 只是因为函数的返回需要做类型判断
from azure.mgmt.resourcegraph.models._models_py3 import QueryResponse


def get_vaults(client: Azure_Client, vaults: Optional[str] = None) -> QueryResponse:
    """获取microsoft.recoveryservices/vaults (Recovery Services 保管库)"""
    if vaults:
        vaults = ','.join(['"%s"' % i for i in vaults.split(',')])
    strQuery = textwrap.dedent("""\
        resources
        | where type == "microsoft.recoveryservices/vaults"
        {vaults}
        | project name, resourceGroup, subscriptionId
        | join kind=leftouter (
            Resourcecontainers
            | where type =~ "microsoft.resources/subscriptions"
            | project SubName=name, subscriptionId
        ) on subscriptionId
        | project-away subscriptionId1
        """).format(
            vaults = f'| where name in ({vaults})' if vaults else ''
        )
    # 去掉空白行
    strQuery = remove_empty(strQuery)
    res = client.resource_graph_query_default(
        strQuery,
        [sub.get("subscription_id") for sub in client.get_subscriptions()],
    )
    return res

def print_and_csv(file=None):
    """输出一行记录"""
    def inner(line):
        print(line)
        if file:
            file.write(line + '\n')
    return inner

def job_add(jobs, job):
    properties = job.get('properties')
    if not properties:
        return
    container_name = properties.get('containerName')
    if not container_name:
        return
    if container_name not in jobs:
        jobs[container_name] = [properties, properties]
    else:
        jobs[container_name][1] = properties

def init_argparse(args: Optional[List[str]] = None) -> argparse.Namespace:
    """初始化：命令行参数"""
    parser = argparse.ArgumentParser(description='导出备份清单')
    
    parser.add_argument('-e', '--envfile', help="存有用户认证信息的env文件的文件名")
    parser.add_argument('--vault', help='限定保管库, 多个名称用逗号分隔')
    parser.add_argument('--start', help='Start time')
    parser.add_argument('--end', help='End time')
    parser.add_argument('--csvfile', dest='dev_csvfile', help="仅限本地使用，输出一份到文件")

    args = parser.parse_args(args)
    return args

def main(args: Optional[List[str]] = None) -> None:
    """主函数"""
    # 初始化命令行参数
    if args is None:
        # 蓝鲸作业平台有BUG，重做的任务，空着的参数里都会被填充null
        args = [arg for arg in sys.argv[1:] if arg != 'null' and not arg.endswith('=null')]
    args = init_argparse(args)
    if not args.envfile:
        return job_fail("请提供存有用户认证信息的env文件的文件名")
        
    # 初始化环境变量
    envs = init_environs(args.envfile)
    if not envs:
        return job_fail("env文件解密错误")
    if envs['runtime_env'] == 'blueking':
        for key in args.__dict__.keys():
            if key.startswith('dev_'):
                v = getattr(args, key)
                if v:
                    print(f'{key}={v}: 不支持的命令行参数，已经忽略。', file=sys.stderr)
                    setattr(args, key, None)
                    
    # 实例化客户端
    CLOUD = getattr(msrestazure.azure_cloud, envs['cloud_name'])
    client = Azure_Client(CLOUD)

    vaults = get_vaults(client, args.vault)
    # 检查数量
    if not len(vaults.data):
        return job_fail("未匹配到任何保管库，建议检查过滤名称。")

    if args.dev_csvfile:
        csvfile = open(args.dev_csvfile, 'w', encoding='utf-8')
        print_line = print_and_csv(csvfile)
    else:
        print_line = print_and_csv()
    title = ['vault_sub', 'vault_resourceGroup', 'vault_name', 'vm_resourceGroup', 'vm_name', 'last', 'first']
    print_line(','.join(title))

    # 调用REST API请求 Backup  - List
    # 遍历每一个 保管库
    for vault in vaults.data:
        # pylint: disable = no-member
        res = client.backupjobs_yield_all(
            subscription_id=vault['subscriptionId'],
            resource_group_name=vault['resourceGroup'],
            vault_name=vault['name'],
            start=args.start,
            end=args.end,
        )
        jobs = {}
        # 遍历每一次请求的结果：就是一批记录。记录数过多，一次请求无法全部返回时，只能返回一部分和
        for r in res:
            value = r.json().get('value')
            # 遍历返回记录里，没一条记录
            for job in value:
                job_add(jobs, job)

        for j in jobs.values():
            out = []
            out.append(vault['SubName'])
            out.append(vault['resourceGroup'])
            out.append(vault['name'])
            out.append(j[0]['containerName'].split(';')[1])
            out.append(j[0]['entityFriendlyName'])
            time0 = j[0]['startTime']
            local_time0 = date_parse(time0).astimezone().strftime("%Y-%m-%dT%H:%M:%SZ%z")
            out.append(local_time0)
            time1 = j[1]['startTime']
            local_time1 = date_parse(time1).astimezone().strftime("%Y-%m-%dT%H:%M:%SZ%z")
            out.append(local_time1)
            print_line(','.join(out))

    if args.dev_csvfile:
        csvfile.close()


if __name__ == '__main__':

    job_start()

    ###### 可在此处开始编写您的脚本逻辑代码
    ###### iJobs中执行脚本成功和失败的标准只取决于脚本最后一条执行语句的返回值
    ###### 如果返回值为0，则认为此脚本执行成功，如果非0，则认为脚本执行失败

    main()
